'''
An RPG Modification for Source games using Source Python.

    Copyright (C) 2012 Steven Hartin

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

Created on 27 Nov 2012

@author: Steven Hartin
@file: ../sourcerpg/plugin/plugin.py
'''

from time import time

from Source import Player as SourcePlayer
from events import Event
from players.helpers import index_from_userid
from players.entity import PlayerEntity

from sourcerpg.logging import logger, auto_logger, loglevels
from sourcerpg.utils.baseclass import BaseClass
from sourcerpg.database import database
from sourcerpg.player import players, Player
from sourcerpg.config import config
from sourcerpg.skills import skills
from sourcerpg.popuplib import EasyMenu

__all__ = ["SourceRPG"]


class SourceRPG(BaseClass):

    def __init__(self):
        pass

    def __del__(self):
        logger.log("=" * 60)
        logger.log("SourceRPG - Unloading", writetime=True)
        database.commit_cache()
        logger.log("=" * 60)

    @staticmethod
    @Event
    def player_activate(GameEvent):
        userid = GameEvent.GetInt("userid")
        player = PlayerEntity(index_from_userid(userid))
        database.init_player_cache(player)
        players[userid] = Player(index_from_userid(userid))
        logger.log("Player: %s (uid: %d) activated" % (
            players[userid], userid), level=loglevels.INFO_LEVEL_2)

    @staticmethod
    @Event
    def player_disconnect(GameEvent):
        userid = GameEvent.GetInt("userid")
        player = PlayerEntity(index_from_userid(userid))
        database["players"][player.uniqueid]["lastconnected"] = time()
        database["players"][player.uniqueid]["name"] = player.name
        num_players = 0
        for player in SourcePlayer.Players():
            num_players += 1
        if num_players == 0:
            database.commit_cache()
        if userid in players:
            del players[userid]

    @staticmethod
    @Event
    def player_death(GameEvent):
        """Reward the attacker for the kill, annoying math follows..."""
        victim = players[GameEvent.GetInt("userid")]
        attacker = players[GameEvent.GetInt("attacker")]

        # Obtain the configuration values
        kill_config = config["xp"]["events"]["kill"]

        # Base XP config
        kill_xp = kill_config.as_int("kill_xp")
        level_exponent = kill_config.as_float("level_exponent")

        # Level Difference config
        exponent_diff = kill_config.as_float("exponent_diff")
        linear_diff = kill_config.as_float("linear_diff")
        max_level_diff = kill_config.as_float("max_level_diff")
        min_level_diff = kill_config.as_float("min_level_diff")

        # XP Difference reduction modifier config
        linear_diff_reduction = kill_config.as_float("linear_diff_reduction")
        level_diff_reduction_exponent = \
            kill_config.as_float("level_diff_reduction_exponent")
        level_diff_reduction_max = kill_config.as_float(
            "level_diff_reduction_max")
        level_diff_reduction_min = kill_config.as_float(
            "level_diff_reduction_min")

        # The difference between the levels clamped so that:
        # min_level_diff <= level_diff <= max_level_diff
        level_diff = min(max(victim.get_level()[0] - attacker.get_level()[0],
            min_level_diff), max_level_diff)
        reduction_sign = -1 if level_diff < 0 else 1
        linear_diff *= reduction_sign

        # Base XP = <initial kill xp> * <victims level> ^ exponential_growth
        base_xp = (kill_xp * attacker.get_level()[0] ** level_exponent)

        # We begin the reduction modifier by linearly dividing the attacker's
        # level by the linear diff reduction value.
        # Reduction modifier is the reduction level ^ reduction exponent, and
        # then clamped such that:
        # level_diff_reduction_min <= reduction modifier <= level_diff_max
        reduction_level = attacker.get_level()[0] / linear_diff_reduction
        reduction_modifier = (min(level_diff_reduction_max,
            reduction_level ** level_diff_reduction_exponent +
            level_diff_reduction_min))

        # Bonus XP = level_diff ^ exponential_diff * linear_diff * base kill xp
        bonus_xp = abs(level_diff) ** exponent_diff * linear_diff * kill_xp

        total_xp = base_xp + bonus_xp * reduction_modifier
        total_xp = int(max(kill_xp, total_xp))
        attacker.send_message("Awarded %d xp (basexp: %d, reduction_modifier:"
            " %.2f, bonus_xp: %d)" % (total_xp, base_xp,
            reduction_modifier, bonus_xp))
        attacker.add_xp(total_xp)

    @staticmethod
    @Event
    def round_end(GameEvent):
        database.commit_cache()

    @staticmethod
    @Event
    def player_spawn(GameEvent):
        userid = GameEvent.GetInt("userid")
        if userid in players:
            players[userid].send_message(
                "[SourceRPG] You are on level %d (%d/%d XP)" % (
                players[userid].get_level()))
            for skill_name, skill in skills.items():
                if skill_name in players[userid]["skills"]:
                    skill.apply_skill(players[userid],
                        players[userid]["skills"][skill_name]["level"])
